package org.virbraligo.vboxjavaservice;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

public class VBoxJavaService {

	static {
		System.setProperty("java.util.logging.config.file",
				VBoxJavaService.class.getResource("/logging.properties")
						.getFile());
	}
	static Logger logger = Logger.getLogger(VBoxJavaService.class.getName());

	static String vmConfigHome = null;

	static String vmname = null;
	static String shutdown_policy = null;

	public static boolean active = true;

	/**
	 * @param args
	 */
	public static void main(final String[] args) throws Exception {
		// Process p = Runtime.getRuntime().exec(new
		// String[]{"cmd.exe","","D:\\bin\\jdk1.7\\bin\\java.exe -version>D:\\tmp\\output.txt"});
		try {
			checkArgs(args);

			Runtime.getRuntime().addShutdownHook(new Thread() {
				public void run() {
					synchronized (HeadlessProcessMonitor.headlesspidCheckLock) {
						shutdown();
					}
				}
			});

			startvm();

		} catch (Exception e) {
			logger.log(Level.SEVERE, e.getMessage(), e);
		}

	}

	private static void startvm() throws Exception {
		logger.info("startvm " + vmname);
		String startcmd = "VBoxManage -nologo startvm " + vmname
				+ " --type headless";
		logger.info("start cmd: " + startcmd);
		String result = execute(startcmd);
		logger.info(result);
		if (result.toLowerCase().indexOf("could not find a registered") > 0) {
			String registerCmd = "VBoxManage registervm \"" + vmConfigHome
					+ "/" + vmname + ".vbox\"";
			logger.info("try to register " + vmname + ", and will restart "
					+ vmname);
			logger.info("register cmd: " + registerCmd);
			String regResult = execute(registerCmd);// to fix error: VBoxManage:
													// error: Could
			// not
			// find a registered machine named
			logger.info(regResult);
			startvm();
		}
		if (result.toLowerCase().indexOf("failed to load unit 'hgcm'") > 0) {
			String clearCmd = "VBoxManage discardstate " + vmname;
			logger.info("clear cmd: " + clearCmd);
			String clearResult = execute(clearCmd);
			logger.log(Level.INFO, clearResult);
			startvm();
		} else {
			new LogMonitor(vmConfigHome, new Date()).start();
		}
	}

	private static void shutdown() {
		logger.info("shutdown hook activated, headlessAlive="
				+ HeadlessProcessMonitor.headlessAlive);
		if (HeadlessProcessMonitor.headlessAlive) {
			String stopcmd = "VBoxManage controlvm " + vmname + " "
					+ shutdown_policy;
			try {
				logger.info("shutdown " + vmname + ", cmd: " + stopcmd);
				String result = execute(stopcmd);
				logger.info(result);
				active = false;
			} catch (Exception e) {
				logger.log(Level.SEVERE, e.getMessage(), e);
			}
		}
	}

	private static void checkArgs(String[] args) {
		if (args.length != 2) {
			logger.severe("VBoxJavaService need 3 params: vmname(uuid), shudown_policy, vm_config_home");
			System.exit(1);
		} else {
			shutdown_policy = args[0];
			vmConfigHome = args[1];
			vmname = new File(vmConfigHome).getName();
			logger.info("vmname: " + vmname);
			logger.info("shutdown_policy: " + shutdown_policy);
			logger.info("vmConfigHome: " + vmConfigHome);
		}
	}

	public static String execute(String cmd) throws Exception {
		Process p = Runtime.getRuntime().exec(cmd);
		final InputStream[] ins = { p.getInputStream(), p.getErrorStream() };
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < ins.length; i++) {
			InputStream in = ins[i];
			try {
				byte[] b = new byte[1000];
				int len = 0;
				ByteArrayOutputStream bao = new ByteArrayOutputStream();
				while ((len = in.read(b)) > 0) {
					bao.write(b, 0, len);
				}
				in.close();
				String s = new String(bao.toByteArray());
				bao.close();
				sb.append(s);
				if (i != ins.length - 1) {
					sb.append("\n");
				}

			} catch (Exception e) {
				logger.log(Level.SEVERE, e.getMessage(), e);
			}
		}
		p.waitFor();
		String s = sb.toString();
		if (s.endsWith("\n")) {
			s = s.substring(0, s.length() - 1);
		}
		return s;
	}

	public static void copy(File src, File dest) throws IOException {
		FileInputStream in = new FileInputStream(src);
		FileOutputStream out = new FileOutputStream(dest);
		byte[] buffer = new byte[1024];
		while (true) {
			int ins = in.read(buffer);
			if (ins == -1) {
				in.close();
				out.flush();
				out.close();
				break;
			} else {
				out.write(buffer, 0, ins);
			}
		}
	}

}
